package Dungeon;

import java.util.Iterator;
import java.util.Set;


import ConditionPackage.Condition;
import ConditionPackage.DefaultCondition;
import SquarePackage.Direction;
import SquarePackage.Square;
import SquarePackage.TeleportableSquare;

/**
 * Class for a shaft, this is a dungeon which can only vary in one coordinate.
 * @invar	this type of Dungeon can not have Rocks
 * 			| foreach(square) in getAllSquares() square instance of TeleportableSquare.class
 * @invar	There can not be Doors inside this dungeon except on the borders of the dungeon,
 * 			this is when one of the Dimensions of the Square is at his maximum or minimum
 * 			| hasProperDoors

 *
 * @author 	Brecht Reynders en Florian Hendrickx
 * @version	1.0
 */
public class Shaft<E extends TeleportableSquare> extends Dungeon<E> {
	/**
	 * Initializes a new shaft with the given coordinates for the maximum dimension.
	 * 
	 * @param	maxX
	 * 			The maximum x-coordinate value.
	 * @param	maxY
	 * 			The maximum y-coordinate value.
	 * @param	maxZ
	 * 			The maximum z-coordinate value. 
	 * @param	direction
	 * 			The direction for this shaft.			
	 * @effect	The given coordinates are the coordinates of the maximum dimension.
	 * 			| Dimension(maxX, maxY, maxZ) == new.getMaxDimension() 
	 * @post 	The given direction is the new direction this shaft can vary in.
	 * 			| new.getDirection = direction
	 */
	public Shaft(long maxX, long maxY, long maxZ ) {
		super(maxX, maxY, maxZ);
		
	}
	/**
	 * Initializes a new shaft with the given dimension as maximum dimension.
	 * 
	 * @param 	maxDimension
	 * 			The maximum dimension
	 * @param	direction
	 * 			The direction for this shaft.
	 * @effect	The given maximum dimension is the new max dimension.
	 * 			| new.getMaxDimension = maxDimension
	 * @post 	The given direction is the new direction for this shaft.
	 * 			| new.getDirection = direction
	 */
	public Shaft(Dimension maxDimension) {
		super(maxDimension);
		}
	/**
	 * this method checks the specified invariant for doors, they can only be on the borders of the dungeon
	 * @param square
	 * 			t
	 * @return
	 *			| if(foreach(square) in getAllSquares(): if(square.hasBorder(direction) && square.getBorderAt(direction) instanceof Door.class)
	 *			| !getAllSquares().contains(square.getBorderAtDirection(direction).getOtherSquare(square)) 
	 */
	private boolean hasProperDoors() {
		Condition condition = new DefaultCondition();
		Iterator<? extends E> it = iterator(condition);
		while(it.hasNext()){
			Square square = it.next();
			for(Direction direction : Direction.values()){
				if(direction == getDirection() || direction == getDirection().getOpposite()){
					if((square.hasBorder(direction)) && (square.hasDoorAt(direction))){
						if (!(contains(square.getBorderAt(direction).getOtherSquare(square))))
							return false;
					}
				}
			}
		}
		return true;
	}
	
	/**
	 * Checks whether the given square is in this shaft or not.
	 * @param 	square
	 * 			The square to check.
	 * @return	True if and only if the given square is in the dungeon.
	 * 			| getDimensionOf(square) != null
	 */
	private boolean contains(Square square){
		Condition condition = new DefaultCondition();
		Iterator<? extends E> it = iterator(condition);
		while(it.hasNext()){
			if(it.next() == square)
				return true;
		}
		return false;
	}
	
	/**
	 * this method overrides the addsquare method so we are sure that there are no doors shared by two squares within this shaft
	 * @post if(!hasProperDoors) after super.addSquare then( new.getAllSquares.contains(square))
	 * 
	 */
	@Override
	public void addSquare(E square,Dimension dimension){
		super.addSquare(square, dimension);
		if(!hasProperDoors()){
			removeSquareAt(dimension);
		}
	}
	/**
	 * method to set a number of squares in one time by given a starting position and extending until the preferred length is reached
	 * @param dimension the starting dimension
	 * @param baseSquareShaft the Square the other squares will resemble
	 * @param length	the amount of Squares to set.
	 * @param increase	true means increasing dimension, false means decreasing
	 */
	public void fillInDirection(Dimension dimension, E baseSquare, int length, boolean increase) {
		Direction direction;
		int i = 0;
		if(increase){ direction = getDirection();}
		else{direction=getDirection().getOpposite();}
		addSquare(baseSquare, dimension);
		while(i<length){
			dimension = dimension.getDimensionIn(direction);
			if(!isValidDimension(dimension)){
				i=length;
			}
			else{
			E square = (E) baseSquare.clone();
			addSquare(square, dimension);
			i++;
			}
		}
	}
	/**
	 * method to check if this Shaft has Valid max Dimensions
	 * @return True if and only if super.isValidMaxDimension returns True and two of the 3 MaxDimensions are Zero
	 * 			| if super.isValidMaxDimension(dimension)&& ((maxX=0&&maxY=0)||(maxX=0&&maxZ=0)||(maxY=0&&maxZ=0))
	 * 			| then result=true
	 */
	@Override
	protected boolean isValidNewMaxDimension(Dimension dimension){
		Boolean returnBoolean = super.isValidNewMaxDimension(dimension);
		if(!((dimension.getXPosition() == 0 && dimension.getYPosition() == 0) || (dimension.getXPosition() == 0 && dimension.getZPosition() == 0) || (dimension.getYPosition() == 0 && dimension.getZPosition()== 0))){
			returnBoolean = false;
		}
		return returnBoolean;
	}
	/**
	 * method to get the varying direction of this Square.
	 * @return Direction
	 * 			the Direction this shaft varies in, off course it also varies in the opposite one...
	 */
	protected Direction getDirection(){
		if(this.getMaxDimension().getXPosition()!=0){
			return Direction.EAST;
		}
		if(this.getMaxDimension().getYPosition()!=0){
			return Direction.SOUTH;
		}
		else{
			return Direction.CEILING;
		}
	}
}
